package com.d3works.apps.base.callable;

import com.d3works.apps.base.service.app.AppBaseService;
import com.d3works.apps.tool.exception.ToolExceptionMessage;
import com.d3works.exception.service.TraceBackService;
import com.d3works.i18n.I18n;
import com.d3works.inject.Beans;
import com.d3works.rpc.ActionResponse;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * Tool class to call specific callable services in a controller.
 *
 * @param <V> the type of the callable service.
 */
public class ControllerCallableTool<V> {

  /**
   * Run the given callable in a separate thread. display any occurring exception in the given
   * response. If the thread is not over before the timeout, send a notification to the user.
   *
   * @param callable a callable service
   * @param response a response available in a controller
   * @return what is returned by the service
   */
  public V runInSeparateThread(Callable<V> callable, ActionResponse response) {
    V result = null;
    ExecutorService executor = Executors.newSingleThreadExecutor();

    // Start thread
    Future<V> future = executor.submit(callable);

    int processTimeout = Beans.get(AppBaseService.class).getProcessTimeout();
    // Wait processTimeout seconds
    try {
      result = future.get(processTimeout, TimeUnit.SECONDS);
    } catch (ExecutionException e) {
      // cause already traced in traceback
      response.setInfo(e.getCause().getMessage());
    } catch (TimeoutException e) {
      response.setNotify(I18n.get(ToolExceptionMessage.PROCESS_BEING_COMPUTED));
    } catch (InterruptedException e) {
      TraceBackService.trace(e);
      Thread.currentThread().interrupt();
    }
    return result;
  }
}
